from PyQt4.QtCore import QAbstractTableModel
from PyQt4.QtCore import QModelIndex
from PyQt4.QtCore import QSettings
from PyQt4.QtCore import QVariant
from PyQt4.QtCore import Qt
from PyQt4.QtCore import SIGNAL
from PyQt4.QtGui import QBrush
from deploy.constants import Config
from deploy.remote.monitor import ServiceStatus
from deploy.remote.windowsService import WindowsService
import logging
import yaml

logger = logging.getLogger(__name__)

class ServiceTableModel(QAbstractTableModel):
    '''Provides service data for table views.
    
    '''

    # COLUMNS is an ordered list of tuples that indicate a unique
    # attribute name, and a displayable name for each column contained
    # in the table model. The attribute name must match an attribute
    # in the MonitoredService class defined below. 
    COLUMNS = [
        ('name', 'Service Name'),
        ('host', 'Host'),
        ('status', 'Status')
    ]

    def __init__(self, parent = None):
        super(ServiceTableModel, self).__init__(parent)
        self.updateServices()

    def updateServices(self):
        '''Update the currently displayed services.'''
        logger.info('Updating services')

        self.emit(SIGNAL('layoutAboutToBeChanged()'))

        serviceConfig = ServiceConfiguration()
        self._monitoredServices = serviceConfig.services

        for service in self._monitoredServices:
            self.connect(service, SIGNAL('statusChanged(PyQt_PyObject)'), self.serviceStatusChanged)

        self.emit(SIGNAL('layoutChanged()'))

    def serviceStatusChanged(self, service):
        self.emit(SIGNAL('serviceStatusChanged(PyQt_PyObject)'), service)

    def headerData(self, section, orientation, role = Qt.DisplayRole):
        '''Get the header data for the model.'''
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            return QVariant(self.COLUMNS[section][1])
        return QVariant()

    def rowCount(self, parent = QModelIndex()):
        '''Get the number of rows in the model.
        
        This is equivalent to the number of services we're tracking.
        '''
        return len(self._monitoredServices)

    def columnCount(self, parent = QModelIndex()):
        '''Get the number of columns in the model.'''
        return len(self.COLUMNS)

    def data(self, index, role = Qt.DisplayRole):
        '''Get the data to display the specified role at the indexed cell.
        
        '''
        # If the index is not valid for any reason, bail out
        if not index.isValid():
            return QVariant()

        # Find the service (row) we're looking at
        service = self._monitoredServices[index.row()]

        if role == Qt.DisplayRole:
            # For the display role, we merely want the data for the cell.
            # This data is available as an attribute on the service.
            attribute = self.COLUMNS[index.column()][0]
            return QVariant(getattr(service, attribute))

        elif role == Qt.BackgroundRole:
            # For the background role, we want to highlight rows that have
            # a status other than RUNNING
            if service.status != ServiceStatus.RUNNING:
                return QVariant(QBrush(Qt.red))

        return QVariant()

    def sort(self, index, order):
        '''Sort table by given column index.'''
        self.emit(SIGNAL('layoutAboutToBeChanged()'))
        self._monitoredServices = sorted(self._monitoredServices,
                                         key = lambda service: getattr(service, self.COLUMNS[index][0]),
                                         reverse = (order == Qt.DescendingOrder))

        self.emit(SIGNAL('layoutChanged()'))


    def getService(self, index):
        return self._monitoredServices[index.row()]

class ServiceConfiguration(object):
    '''Manages the configuration aspects of services.
    
    This object is responsible for reading in the .yaml configuration
    files and creating the appropriate service objects.
    '''

    def __init__(self):
        self.configuredEnvironments = {}
        self.configuredServices = {}

        self.services = []

        self._load()

    def _load(self):
        settings = QSettings()
        configFile = settings.value(Config.ENVIRONMENT_CONFIG_FILE).toString()

        try:
            f = open(configFile)
        except Exception, e:
            logger.error(e)
            return

        for section in yaml.load_all(f):
            if 'service' in section:
                self._loadService(section)
            if 'environment' in section:
                self._loadEnvironment(section)

        self._createServices()

    def _loadService(self, section):
        configuredService = ConfiguredService(**section['service'])
        self.configuredServices[configuredService.name] = configuredService

    def _loadEnvironment(self, section):
        configuredEnvironment = ConfiguredEnvironment(**section['environment'])
        self.configuredEnvironments[configuredEnvironment.name] = configuredEnvironment

    def _createServices(self):
        for configuredEnvironment in self.configuredEnvironments.values():
            for configuredHost in configuredEnvironment.hosts:
                for serviceName in configuredHost.services:
                    configuredService = self.configuredServices[serviceName]

                    service = WindowsService(configuredHost.name,
                                             configuredService.shortName,
                                             configuredService.displayName)

                    self.services.append(service)

class ConfiguredService(object):
    def __init__(self,
                 name = None,
                 type = None,
                 displayName = None,
                 shortName = None):
        self.name = name
        self.type = type
        self.displayName = displayName
        self.shortName = shortName

class ConfiguredEnvironment(object):
    def __init__(self,
                 name = None,
                 hosts = None):
        self.name = name
        self.hosts = self._configureHosts(**hosts)

    def _configureHosts(self, **hosts):
        return [ConfiguredHost(host, services) for host, services in hosts.iteritems()]

class ConfiguredHost(object):
    def __init__(self, name, services):
        self.name = name
        self.services = services
